#ifndef MATRIX_UTILS_H__
#define MATRIX_UTILS_H__

#include <climits>
#include <type_traits>  // round_up_to_power_of_2

#include <regex>
#include <iterator>
#include <string>
#include <vector>
#include <array>

#include "matrix_def.h"
//#include "jenv.h"

//#include <unsupported/Eigen/KroneckerProduct>
#include "unsupported/Eigen/SparseExtra" // saveMarket()
//#include "bmt.h"    // binary mtx file utils
#include "EigenIO.h"  // binary mtx file utils

template <typename T>
int Save_Matrix(const std::string fname, matrix_t<T> &mat) {
  std::ofstream ofile;

  ofile.open(fname, std::ios::out);
  if (!ofile) {
    std::cout << "ERROR: File does not exist (or empty?)\n" << std::endl;
    return (-1);
  }
  ofile << std::scientific << mat;

  std::cerr << " Save Eigen Matrix to '" << fname << "'" << std::endl;
  char buffer[128];
  sprintf(buffer, " rows = %ld, cols = %ld\n", mat.rows(), mat.cols());
  std::cerr << buffer << std::endl;

  ofile.close();

  return 0;
}

template <typename T>
int Save_Matrix(const std::string& fname, Matrix<T, Dynamic, 1> &vec) {
  std::ofstream ofile;

  ofile.open(fname, std::ios::out);
  if (!ofile) {
    std::cout << " ERROR: File does not exist (or empty?)\n" << std::endl;
    return (-1);
  }
  std::cout << " Save Eigen Vector to '" << fname << "'" << std::endl;
  ofile << std::scientific << vec;

  ofile.close();
  return 0;
}

template <typename T>
int Save_Matrix(const std::string fname, compressed_matrix_t<T> &spm) {
  std::ofstream ofile;

  ofile.open(fname, std::ios::out);
  if (!ofile) {
    std::cout << " ERROR: File does not exist (or empty?)\n" << std::endl;
    return (-1);
  }
  std::cout << " Save SparseMatrix to '" << fname << "'" << std::endl;
  ofile << std::scientific << spm;

  ofile.close();
  return 0;
}

template <typename T>
int Save_Matrix(const std::string& fname, DiagonalMatrix<T, Dynamic> &diag) {
  std::ofstream ofile;

  ofile.open(fname, std::ios::out);
  if (!ofile) {
    std::cout << "ERROR: File does not exist (or empty?)\n" << std::endl;
    return (-1);
  }
  std::cout << " Save Eigen DiagonalMatrix to '" << fname << "'" << std::endl;
  ofile << std::scientific << diag.diagonal();

  ofile.close();
  return 0;
}

// std::string get_basename(const std::string &pathname)
// {
//   std::regex e(".+_(ir[xyz]*-)?(\\d+)x(\\d+)x(\\d+).+$");
//   std::cout << " pathname = " << pathname << std::endl;
//   std::cout << " reg_bname = " << std::regex_replace(pathname, e, "$1$2x$3x$4") << std::endl;
//   // std::string bname = std::string(pathname).substr(0,
//   // std::string(pathname).find_last_of("."));
//   auto pos1 = std::string(pathname).find_last_of("_");
//   auto pos2 = std::string(pathname).find_last_of(".");
//   std::string bname = std::string(pathname).substr(pos1, pos2 - pos1);

//   return bname;
// }

inline std::string get_rawname(const std::string &pathname)
{
  std::regex e(".+_(ir[xyz]*-)?(\\d+)x(\\d+)x(\\d+).+$");
  auto result = std::regex_replace(pathname, e, "$1$2x$3x$4");

  return result;
}

template <typename T>
bool save_vector(const std::string &filename, const vector_t<T> &x) {
  return saveMarketVector<vector_t<T>>(x, filename);
}

// template<typename T>
// bool save_matrix(const std::string& filename, const matrix_t<T>& A, int sym =
// 0)
//{
//	return saveMarketVector<matrix_t<T>>(A, filename, sym);
//}

template <typename T>
bool save_sparse_matrix(const std::string &filename,
                        const compressed_matrix_t<T> &A, int sym = 0) {
  std::cout << " Saving market matrix format(mtx) to '" << filename << "'"
            << std::endl;
  return saveMarket<compressed_matrix_t<T>>(A, filename, sym);
}

template <typename T>
bool save_market_matrix(const std::string &filename,
                        const compressed_matrix_t<T> &A, int sym = 0)
{
  std::cout << " Save as market matrix format(mtx) to '" << filename << "'"
            << std::endl;
  return saveMarket<compressed_matrix_t<T>>(A, filename, sym);
}

template <typename T>
bool load_market_matrix(const std::string &filename, compressed_matrix_t<T> &sp_mat)
{
  std::ifstream f(filename.c_str());
  if (f.good())
    std::cout << "\n Loading market matrix format(mtx) from '" << filename << "'" << std::endl;
  else
    std::cout << " File '" << filename << "'" << "not exist!\n" << std::endl;

  return loadMarket<compressed_matrix_t<T>>(sp_mat, filename);
}

template <typename T>
bool save_market_matrix(const std::string &filename, const matrix_t<T> &A,
                        int sym = 0) {
  compressed_matrix_t<T> sp_mat = Eigen::SparseView<matrix_t<T>>(A);
  std::cout << " Saving market matrix format(mtx) to '" << filename << "'"
            << std::endl;

  return saveMarket<compressed_matrix_t<T>>(sp_mat, filename, sym);
}

#endif // MATRIX_UTILS_H__